﻿using System;
using System.Collections.Generic;

using MosCraft.Utilities;

namespace MosCraft.Terrain
{
    public sealed class ChunkManager
    {
        private readonly object syncRoot = new object();
        private Dictionary<ChunkCoordinate, Chunk> chunks;
        private World world;

        /// <summary>
        /// Initialize new class
        /// </summary>
        public ChunkManager(World world)
            :base()
        {
            this.chunks = new Dictionary<ChunkCoordinate, Chunk>();
            this.world = world;
        }

        public IEnumerable<Chunk> Values
        {
            get
            {
                lock (syncRoot)
                {
                    return chunks.Values;
                }
            }
        }

        /// <summary>
        /// property  that allows direct accsess to the chunk
        /// dictionary, allows chunks to be get or set.
        /// </summary>
        /// <param name="x">The X coord</param>
        /// <param name="z">The Z coord</param>
        /// <returns>The chunk based on the coordinates entered</returns>
        public Chunk this[int x, int z]
        {
            get
            {
                lock (syncRoot)
                {
                    ChunkCoordinate index = new ChunkCoordinate(x, z);
                    Chunk chunk;

                    try
                    {
                        chunk = chunks[index];
                        return chunk;
                    }
                    catch
                    {
                        return null;
                    }
                }
                
            }
            set
            {
                lock (syncRoot)
                {
                    ChunkCoordinate index = new ChunkCoordinate(x, z);
                    chunks[index] = value;
                }
            }
        }

        /// <summary>
        /// Checks to see if a chunk exists at the coordinates
        /// </summary>
        /// <param name="x">The X coord</param>
        /// <param name="z">The Z coord</param>
        /// <returns>True if the chunk is exists, false if not</returns>
        public bool chunkExists(int x, int z)
        {
            lock (syncRoot)
            {
                try
                {
                    ChunkCoordinate index = new ChunkCoordinate(x, z);
                    Chunk chunk = chunks[index];

                    if (chunk == null)
                    {
                        return false;
                    }
                    else
                    {
                        return true;
                    }

                }
                catch
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// Trys to remove one value from the database
        /// </summary>
        /// <param name="x">The X coord</param>
        /// <param name="z">The Z coord</param>
        /// <returns>True if the chunk is removed, false if not</returns>
        public bool dropChunk(int x, int z)
        {
            lock (syncRoot)
            {
                if (this.chunkExists(x, z))
                {
                    ChunkCoordinate index = new ChunkCoordinate(x, z);
                    Chunk chunk = chunks[index];
                    lock (chunk)
                    {
                        chunk.Dispose();
                        chunks.Remove(index);
                    }
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        public Chunk addChunk(int x, int z)
        {
            Chunk chunk;

            if (this.chunkExists(x, z))
            {
                chunk = this[x, z];
            }
            else
            {
                chunk = new Chunk(world, x, z);
                chunks.Add(new ChunkCoordinate(x,z), chunk);
            }

            return chunk;
        }

        /// <summary>
        /// Drop all Chunks and resets the 
        /// Dictionary of chunks
        /// </summary>
        public void Clear()
        {
            lock (syncRoot)
            {
                foreach (Chunk chunk in chunks.Values)
                {
                    chunk.Dispose();
                }
                chunks.Clear();
            }
        }


        public override string ToString()
        {
            return string.Format("Chunks: {0}", chunks.Count);
        }
    }
}
